package com.hq.ims.data.service.other;

import com.hq.ims.data.entity.AppPushContent;
import com.hq.ims.data.entity.User;
import com.hq.ims.data.service.BaseAppPushContentService;
import com.hq.ims.data.service.BaseAppPushUserService;
import com.hq.ims.data.service.BaseUserService;
import com.hq.ims.data.utils.LocalConstant;
import com.hq.ims.data.vo.message.*;
import com.hq.utils.exception.CommonException;
import com.hq.utils.util.*;
import com.hq.web.utils.HttpUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * lichaojie
 *
 * @ClassName AppMessagePushService
 * App消息推送
 **/
@Service
@Slf4j
public class AppMessagePushBusService {

    @Value("${userAttribute.uniPush.appId}")
    private String appId;

    @Value("${userAttribute.uniPush.appKey}")
    private String appKey;

    @Value("${userAttribute.uniPush.appSecret}")
    private String appSecret;

    @Value("${userAttribute.uniPush.masterSecret}")
    private String masterSecret;

    @Value("${userAttribute.uniPush.baseUrl}")
    private String baseUrl;

    @Value("${userAttribute.uniPush.authUrl}")
    private String authUrl;

    @Value("${userAttribute.uniPush.pushAllUrl}")
    private String pushAllUrl;

    @Value("${userAttribute.uniPush.pushListUrl}")
    private String pushListUrl;

    @Value("${userAttribute.uniPush.pushListMessageUrl}")
    private String pushListMessageUrl;

    @Value("${userAttribute.uniPush.pushSingleCidUrl}")
    private String pushSingleCidUrl;

    @Resource
    private BaseUserService baseUserService;

    @Resource
    private BaseAppPushContentService baseAppPushContentService;

    @Resource
    private BaseAppPushUserService baseAppPushUserService;

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    /**
     * @param
     * @return : java.lang.String
     * <p>
     * <p>
     * 获取接口前缀
     */
    private String getBaseUrl() {
        return baseUrl + appId;
    }

    /**
     * @param
     * @return : java.lang.String
     * <p>
     * <p>
     * 获取token
     */
    private String getToken() {
        //获取redis
        String token = redisTemplate.opsForValue().get(LocalConstant.APP_PUSH_TOKEN_KEY);
        if (StringUtils.isEmpty(token)) {
            //redis为空则生成token
            generateToken();
            token = redisTemplate.opsForValue().get(LocalConstant.APP_PUSH_TOKEN_KEY);
        }

        return token;
    }

    /**
     * @param
     * @return : void
     * <p>
     * <p>
     * 生成token
     */
    private void generateToken() {
        //创建请求对象
        AuthVo authVo = new AuthVo();

        //初始化请求参数
        authVo.setAppkey(appKey);

        String timestamp = String.valueOf(System.currentTimeMillis());
        authVo.setTimestamp(timestamp);

        String sign = appKey + timestamp + masterSecret;
        String signSHA256 = EncryptDecryptUtils.sha256Encrypt(sign);
        authVo.setSign(signSHA256);

        Map paramMap = JsonUtils.convertObject(authVo, Map.class);

        //组装调用url
        String url = getBaseUrl() + authUrl;
        Map returnMap = HttpUtils.postJson(url, paramMap);
        if (returnMap == null) {
            throw CommonException.build("生成token失败");
        }

        Map data = (Map) returnMap.get("data");
        if (data == null) {
            throw CommonException.build("生成token失败");
        }

        //获取token和到期时间戳
        String token = data.get("token").toString();
        String expireTime = data.get("expire_time").toString();

        //保存到redis中
        redisTemplate.opsForValue().set("appPushToken", token, 20, TimeUnit.HOURS);
    }


    /**
     * @param title  消息标题
     * @param body   消息内容
     * @param extras 自定义Json串
     * @return : void
     * <p>
     * <p>
     * 广播推送
     */
    @Transactional(rollbackFor = Exception.class)
    public void push(@NotNull String title, @NotNull String body, String extras) {
        //组装请求对象
        AppPushData appPushData = assembleData(title, body, extras, null);
        //调用广播推送
        pushAll(appPushData);
    }

    /**
     * @param appPushData 请求对象
     * @return : void
     * <p>
     * <p>
     * UniPush广播推送
     */
    private void pushAll(AppPushData appPushData) {
        //组装请求参数
        Map paramMap = JsonUtils.convertObject(appPushData, Map.class);

        //组装请求头
        Map<String, String> headerMap = new HashMap<>();
        String token = getToken();
        headerMap.put("token", token);

        //组装调用url
        String url = getBaseUrl() + pushAllUrl;
        Map returnMap = HttpUtils.postJson(url, paramMap, headerMap);
        if (returnMap == null) {
            log.error("<<<<<<<<<广播推送发送失败");
        }
    }

    /**
     * @param title       消息标题
     * @param body        消息内容
     * @param extras      自定义Json串
     * @param sendUserIds 推送用户ids
     * @return : void
     * <p>
     * <p>
     * 批量推送
     */
    @Transactional(rollbackFor = Exception.class)
    public void push(@NotNull String title, @NotNull String body, String extras, @NotNull List<String> sendUserIds) {
        //组装请求对象
        AppPushData appPushData = assembleData(title, body, extras, sendUserIds);
        //调用批量推送
        String taskId = pushListMessage(appPushData);
        AudienceVo audienceVo = appPushData.getAudienceVo();
        pushList(taskId, audienceVo);
    }

    /**
     * @param taskId     任务id
     * @param audienceVo 推送设备指定
     * @return : void
     * <p>
     * <p>
     * UniPush批量推送
     */
    private void pushList(String taskId, AudienceVo audienceVo) {
        //组装请求对象
        Map paramMap = new HashMap();
        paramMap.put("audience", audienceVo);
        paramMap.put("is_async", true);
        paramMap.put("taskid", taskId);

        //组装请求头
        Map<String, String> headerMap = new HashMap<>();
        String token = getToken();
        headerMap.put("token", token);

        //组装调用url
        String url = getBaseUrl() + pushListUrl;
        Map returnMap = HttpUtils.postJson(url, paramMap, headerMap);
        if (returnMap == null) {
            log.error("<<<<<<<<<批量推送发送失败");
        }
    }

    /**
     * @param appPushData 请求对象
     * @return : void
     * <p>
     * <p>
     * UniPush创建消息
     */
    private String pushListMessage(AppPushData appPushData) {
        //组装请求参数
        Map paramMap = JsonUtils.convertObject(appPushData, Map.class);
        Object audience = paramMap.get("audience");
        Object audienceVo = paramMap.get("audienceVo");
        if (audience == null && audienceVo != null) {
            paramMap.put("audience", audienceVo);
        }
        log.info("<<<<<<<<<<<<<<" + JsonUtils.objectToString(paramMap));

        //组装请求头
        Map<String, String> headerMap = new HashMap<>();
        String token = getToken();
        headerMap.put("token", token);

        //组装调用url
        String url = getBaseUrl() + pushListMessageUrl;
        Map returnMap = HttpUtils.postJson(url, paramMap, headerMap);
        log.info("<<<<<<<<<<<<<<App推送返回消息" + JsonUtils.objectToString(returnMap));
        if (returnMap == null) {
            log.error("创建消息失败");
        }


        Map data = (Map) returnMap.get("data");
        if (data == null) {
            throw CommonException.build("创建批量推送消息异常");
        }
        String taskid = (String) data.get("taskid");
        if (StringUtils.isEmpty(taskid)) {
            throw CommonException.build("创建批量推送消息异常");
        }

        return taskid;
    }

    /**
     * @param title      消息标题
     * @param body       消息内容
     * @param extras     备注
     * @param sendUserId 发送用户
     * @return : void
     * <p>
     * <p>
     * 发送单推
     */
    @Transactional(rollbackFor = Exception.class)
    public void push(@NotNull String title, @NotNull String body, String extras, @NotNull String sendUserId) {
        //组装请求对象
        AppPushData appPushData = assembleData(title, body, extras, Collections.singletonList(sendUserId));
        //调用单独推送
        pushAlone(appPushData);
    }

    /**
     * @param appPushData 消息对象
     * @return : void
     * <p>
     * <p>
     * UniPush单推
     */
    private void pushAlone(AppPushData appPushData) {
        //组装请求参数
        Map paramMap = JsonUtils.convertObject(appPushData, Map.class);
        Object audience = paramMap.get("audience");
        Object audienceVo = paramMap.get("audienceVo");
        if (audience == null && audienceVo != null) {
            paramMap.put("audience", audienceVo);
        }

        //组装请求头
        Map<String, String> headerMap = new HashMap<>();
        String token = getToken();
        headerMap.put("token", token);

        //组装调用url
        String url = getBaseUrl() + pushSingleCidUrl;
        Map returnMap = HttpUtils.postJson(url, paramMap, headerMap);
        if (returnMap == null) {
            log.error("<<<<<<<<<单独推送发送失败");
        }
        log.info(returnMap.toString());
    }

    /**
     * @param title       消息标题
     * @param body        消息内容
     * @param extras      自定义Json串
     * @param sendUserIds 用户ids
     * @return : void
     * <p>
     * <p>
     * 组装请求对象
     */
    private AppPushData assembleData(String title, String body, String extras, List<String> sendUserIds) {
        title = StringUtils.appendEllipsis(title, 50);
        body = StringUtils.appendEllipsis(body, 256);
        //创建请求对象
        AppPushData appPushData = new AppPushData();

        //请求唯一标识号
        appPushData.setRequest_id(SeqUtils.generateUuid());

        //推送设备指定
        if (ListUtils.isNotExistsEle(sendUserIds)) {
            appPushData.setAudience("all");
        } else {
            AudienceVo audienceVo = new AudienceVo();

            List<String> cids = new ArrayList<>();
            List<String> notSendUserIds = new ArrayList<>();
            sendUserIds.forEach(userId -> {
                User user = baseUserService.getById(userId);
                if (user == null) {
                    return;
                }

                if (StringUtils.isEmpty(user.getPushId())) {
                    notSendUserIds.add(userId);
                } else {
                    cids.add(user.getPushId());
                }
            });

            if (ListUtils.isExistsEle(notSendUserIds)) {
                //保存APP推送消息内容
                AppPushContent appPushContent = baseAppPushContentService.initData(title, body, extras);
                //保存待发送的用户
                baseAppPushUserService.initData(appPushContent.getAppPushContentId(), notSendUserIds);
            }

            audienceVo.setCid(cids);
            appPushData.setAudienceVo(audienceVo);
        }

        //个推推送消息参数
        PushMessageVo pushMessageVo = new PushMessageVo();
        NotificationVo notificationVo = new NotificationVo();

        notificationVo.setTitle(title);
        notificationVo.setBody(body);
        notificationVo.setClick_type("payload");
        notificationVo.setPayload(extras);

        //发送透传消息
        //pushMessageVo.setTransmission(JsonUtils.objectToString(notificationVo));
        //发送通知消息
        pushMessageVo.setNotification(notificationVo);
        appPushData.setPush_message(pushMessageVo);

        //厂商推送消息参数
        PushChannelVo pushChannelVo = new PushChannelVo();

        //Ios信息
        IosVo iosVo = new IosVo();
        ApsVo apsVo = new ApsVo();
        AlertVo alertVo = new AlertVo();

        alertVo.setTitle(title);
        alertVo.setBody(body);

        apsVo.setAlert(alertVo);
        iosVo.setAps(apsVo);
        iosVo.setPayload(extras);
        pushChannelVo.setIos(iosVo);

        //android信息
        AndroidVo androidVo = new AndroidVo();
        UpsVo upsVo = new UpsVo();
        NotificationVo notification = new NotificationVo();

        notification.setTitle(title);
        notification.setBody(body);
        notification.setClick_type("payload");
        notification.setPayload(extras);

        //发送透传消息
        //upsVo.setTransmission(JsonUtils.objectToString(notification));
        //发送通知消息
        upsVo.setNotification(notification);
        androidVo.setUps(upsVo);
        pushChannelVo.setAndroid(androidVo);

        appPushData.setPush_channel(pushChannelVo);

        return appPushData;
    }
}
